﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace LittleBee.Utils
{
	public abstract class ObjectPool<T> : IDisposable
		where T : class
	{

		UInt32 _nCapacity = 100;
		UInt32 _nCountMax = 0;
		UInt32 _nCount = 0;
		readonly ConcurrentBag<T> _oFreeConnectionList = new ConcurrentBag<T>();
		readonly ConcurrentBag<T> _oBusyConnectionList = new ConcurrentBag<T>();

		public UInt32 Capacity { get => _nCapacity; set => _nCapacity = value; }
		public UInt32 Count { get => _nCount; }
		public UInt32 CountMax { get => _nCountMax; }

		public ObjectPool(UInt32 nCap = 100)
		{
			_nCapacity = nCap;
		}

		protected abstract T CreateObjcect();
		protected abstract void DisposeObjcect(T obj);

		public void Dispose()
		{
			while (_oBusyConnectionList.Count > 0)
				Thread.Sleep(500);

			foreach (var item in _oFreeConnectionList)
			{
				DisposeObjcect(item);
			}
		}

		private readonly int TryCountMax = 5;

		public virtual T GetObject()
		{
			T oRet;
			int nTryCount = 0;

			do
			{
				if (!_oFreeConnectionList.TryTake(out oRet!) && _nCount < _nCapacity)
				{
					oRet = CreateObjcect();
					++_nCount;
					_nCountMax = _nCount;
					break;
				}
				++nTryCount;
				Thread.Sleep(100);
			} while (oRet == null && nTryCount < TryCountMax);

			if (oRet == null)
				throw new IndexOutOfRangeException($"Object pool is full and all is busy");

			_oBusyConnectionList.Add(oRet);

			return oRet;
		}

		public virtual void PutObject(T obj)
		{
			T? oCurr;
			
			if (_oBusyConnectionList.TryTake(out oCurr) == false || oCurr.Equals(obj) == false)
			{
				if (oCurr != null)
					_oBusyConnectionList.Add(oCurr);

				throw new KeyNotFoundException($"not found specified object： {obj.ToString()}, oCurr :{oCurr?.ToString()}");
			}

			_oFreeConnectionList.Add(obj);
		}
	}
}
